home *** CD-ROM | disk | FTP | other *** search
Text File | 1996-06-23 | 8.6 KB | 341 lines | [TEXT/CWIE] |
- #include "CBoid.h"
- #include "FlockDrawing.h"
- #include "FinderRegistry.h"
-
- extern long gWindowView;
-
- //!•
- GetIconBits(short id, Handle *theBits, Handle *theMaskBits);
-
-
- CBoid* CBoid::InitBoid( Rect *worldRect, Handle theBits, Handle theMaskBits, Point position)
- {
- short wid, hgt, err;
- Rect rect;
-
- // Set other Particle variables
- wid = worldRect->right - worldRect->left;
- hgt = worldRect->bottom - worldRect->top;
- this->fPosition.h = position.h;
- this->fPosition.v = position.v;
- this->fVelocity.h = 0;
- this->fVelocity.v = 0;
- this->fStartPoint = position;
- this->fHome = false;
- this->Reset(); // initializes the Neighbor record to none and sets acceleration to zero
- err = this->InitBits(theBits, theMaskBits);
-
- if(err) SysBeep(10);
- return(this);
- }
-
- void CBoid::GetBoidPos(FloatPoint *pos)
- {
- *pos = this->fPosition;
- }
-
- void CBoid::GetBoidVel(FloatPoint *vel)
- {
- *vel = this->fVelocity;
- }
-
- // Re-initializes the Neighbor record and sets acceleration to zero
- void CBoid::Reset(void)
- {
- // The neighbors
- fNaybs.fNum = 0;
- fNaybs.fAvgPos.h = 0;
- fNaybs.fAvgPos.v = 0;
- fNaybs.fAvgVel.h = 0;
- fNaybs.fAvgVel.v = 0;
- fNaybs.fAvgDistSquared = 0;
- fNaybs.fNearestPos.h = 0;
- fNaybs.fNearestPos.v = 0;
- fNaybs.fNearestDistSquared = 2000000; // For later "are we closer than the last?" comparisons.
- // We want the answer to the first one to be yes.
-
- // The acceleration
- fAcceleration.h = 0;
- fAcceleration.v = 0;
- }
-
- // This routine does the flock thing to get the new position,
- // wraps the position in the given Rect (toroidal world, don't ya know), and returns
- // the new position in "newPos".
-
- void CBoid::Move(FloatPoint *newPos, Rect *flockRect, ControlRec *controls, double comfydistsq)
- {
- double wid, hgt;
- Rect rect;
- short rposX, rposY;
- GrafPtr currentPort;
-
- // If we're not home, do the usual
- if(!this->fHome)
- {
- // Look at neighbors and calculate new acceleration.
- // This is where the flocking really happens.
- NewAccel(comfydistsq, controls);
-
- wid = flockRect->right - flockRect->left;
- hgt = flockRect->bottom - flockRect->top;
-
- // Keep the velocity within limits
- Clamp(&this->fVelocity, controls->fMaxVelocityCtl);
-
- // Step the boid
- this->NextStep();
-
- // Wrap the boid
- if(this->fPosition.h < flockRect->left)
- this->fPosition.h += wid;
- else if(this->fPosition.h > flockRect->right)
- this->fPosition.h -= wid;
- // Same for v
- if(this->fPosition.v < flockRect->top)
- this->fPosition.v += hgt;
- else if(this->fPosition.v > flockRect->bottom)
- this->fPosition.v -= hgt;
- }
- else // we're near home, settle in
- {
- this->fPosition.h = this->fStartPoint.h;
- this->fPosition.v = this->fStartPoint.v;
- }
-
- // return new position
- *newPos = this->fPosition;
-
- // convert new position to a rect
- rposX = rint(this->fPosition.h);
- rposY = rint(this->fPosition.v);
- rect.left = rposX;
- rect.top = rposY;
- if(gWindowView == pIconBitmap)
- {
- rect.right = rposX + 32;
- rect.bottom = rposY + 32;
- }
- else if(gWindowView == pSmallIcon)
- {
- rect.right = rposX + 16;
- rect.bottom = rposY + 16;
- }
-
- // Draw!!
- HLock(this->fTheMap);
-
- // Let mask region catch up
- OffsetRgn(this->fTheMaskRgn, rposX, rposY);
-
- // OK, slam those bits
- GetPort(¤tPort);
- CopyBits((BitMap *)*this->fTheMap, ¤tPort->portBits,
- &(**(BitMapHandle)this->fTheMap).bounds, &rect, srcCopy, this->fTheMaskRgn);
- HUnlock(this->fTheMap);
-
- // reset rgn for next time
- OffsetRgn(this->fTheMaskRgn, -rposX, -rposY);
-
- // Inval the rect
- MarkRect(&rect);
-
- // Reset for next time
- Reset();
- }
-
-
- // This routine gets acceleration requests from each of the three "rules" in order,
- // and arbitrates the results: Each request is added into an Acceleration accumulator
- // (AccAcc), and it's magnitude is added into another, scalar accumulator (MagAcc).
- // When the accumulated magnitudes equal or exceed the maximum allowable "oompf,"
- // further requests are denied (actually, not even requested). The final accumulated
- // acceleration is trimmed back to a reasonable value and then set as the new
- // acceleration.
- void CBoid::NewAccel(double comfydistsq, ControlRec *controls)
- {
- long leftover = controls->fMaxEffortCtl;
- FloatPoint AccAcc = {0, 0}, request = {0, 0};
-
- // If there are no neighbors, do nothing
- if(this->fNaybs.fNum == 0)
- return;
-
- // First, avoid collisions
- if(leftover > 0)
- {
- RunAway(comfydistsq, &request);
- Clamp(&request, controls->fAvoidMaxCtl);
- AccAcc = request;
-
- // Subtract the "energy" used by this request
- leftover -= VecMagSq(request.h, request.v);
-
- // if something leftover, do next rule
- if(leftover > 0)
- {
- // Match the neighbors average velocity
- MatchVel(&request);
- Clamp(&request, controls->fMatchMaxCtl);
- AccAcc.h += request.h;
- AccAcc.v += request.v;
-
- // Subtract the "energy" used by this request
- leftover -= VecMagSq(request.h, request.v);
-
- // if something leftover, do next rule
- if(leftover > 0)
- {
- // Try to move toward the "center" of the neighbors
- CatchUp(&request);
- Clamp(&request, controls->fCenterMaxCtl);
- AccAcc.h += request.h;
- AccAcc.v += request.v;
- }
- }
- // Trim new acceleration
- Clamp(&AccAcc, controls->fMaxAccelCtl);
- }
- this->fAcceleration = AccAcc;
- }
-
-
- void CBoid :: RunAway(double comfydistsq, FloatPoint *request)
- {
- double scaler;
- double distsq;
- double biggest;
-
- // First get the distance squared to the nearest neighbor
- distsq = (fNaybs.fNearestDistSquared > 0) ? fNaybs.fNearestDistSquared : 0.01; // To avoid dividing by 0
-
- // if distsq > comfydistsq, request no change
- if(distsq > comfydistsq)
- {
- request->h = 0;
- request->v = 0;
- }
- else // Check it out
- {
- // get the vector pointing from the nearest neighbor to us
- request->h = fPosition.h - fNaybs.fNearestPos.h;
- request->v = fPosition.v - fNaybs.fNearestPos.v;
-
- biggest = (fabs(request->v) > fabs(request->h)) ? fabs(request->v) : fabs(request->h);
- if(biggest == 0)
- {
- request->h = Random() % 4;
- request->v = Random() % 4;
- biggest = (fabs(request->v) > fabs(request->h)) ? fabs(request->v) : fabs(request->h);
- if(biggest == 0) biggest = 1;
- }
- scaler = comfydistsq / biggest;
- request->h = (request->h * scaler) / distsq;
- request->v = (request->v * scaler) / distsq;
- }
- }
-
- void CBoid :: MatchVel(FloatPoint *request)
- {
- // Just return the difference from the neighbors' average velocity
- request->h = (fNaybs.fAvgVel.h - fVelocity.h);
- request->v = (fNaybs.fAvgVel.v - fVelocity.v);
- }
-
- void CBoid :: CatchUp(FloatPoint *request)
- {
- // Just return the vector pointing toward the center of the neighbors
- request->h = (fNaybs.fAvgPos.h - fPosition.h);
- request->v = (fNaybs.fAvgPos.v - fPosition.v);
- }
-
- //----------------------------------------------
- // •• Icon junk
- //----------------------------------------------
- OSErr CBoid :: InitBits(Handle theBits, Handle theMaskBits)
- {
- OSErr err;
- Rect mapRect;
- long bitsSize, maskOffset;
- Handle theMaskBitmap;
-
- if(gWindowView == pIconBitmap)
- {
- SetRect(&mapRect, 0, 0, 32, 32);
- maskOffset = 128;
- }
- else if(gWindowView == pSmallIcon)
- {
- SetRect(&mapRect, 0, 0, 16, 16);
- maskOffset = 32;
- }
- else
- return -1;
-
- if(theBits != nil)
- {
- bitsSize = BuildMap(&fTheMap, &mapRect, 8);
- if(bitsSize == 0) return memFullErr;
- fTheBits = NewPtr(bitsSize);
- if(fTheBits == nil) return memFullErr;
-
- HLock(theBits);
- BlockMove(*theBits, fTheBits, bitsSize);
- HUnlock(theBits);
- }
- else // if theBits is nil, no 8 bit icon, use the black and white icon
- {
- bitsSize = BuildMap(&fTheMap, &mapRect, 1);
- if(bitsSize == 0) return memFullErr;
- fTheBits = NewPtr(bitsSize);
- if(fTheBits == nil) return memFullErr;
-
- HLock(theMaskBits);
- BlockMove(*theMaskBits, fTheBits, bitsSize);
- HUnlock(theMaskBits);
- }
-
- ((BitMap *)(*fTheMap))->baseAddr = fTheBits;
-
- // Set up mask Rgn
- bitsSize = BuildMap(&theMaskBitmap, &mapRect, 1);
- if(bitsSize == 0) return memFullErr;
-
- HLock(theMaskBits);
- HLock(theMaskBitmap);
- ((BitMap *)(*theMaskBitmap))->baseAddr = (*theMaskBits) + maskOffset;
-
- fTheMaskRgn = NewRgn();
- BitMapToRegion(fTheMaskRgn, (BitMap *)(*theMaskBitmap));
-
- HUnlock(theMaskBits);
- HUnlock(theMaskBitmap);
- DisposeHandle(theMaskBitmap);
-
- return noErr;
- }
-
- void CBoid :: Nudge(void)
- {
- this->fPosition.h += Random() % 3;
- this->fPosition.v += Random() % 3;
- }
-
- Boolean CBoid :: HeadHome(void)
- {
- // Still getting there
- this->fNaybs.fNum = 1;
- this->fNaybs.fAvgPos.h = this->fStartPoint.h;
- this->fNaybs.fAvgPos.v = this->fStartPoint.v;
- this->fNaybs.fAvgVel.h = 0;
- this->fNaybs.fAvgVel.v = 0;
- this->fNaybs.fAvgDistSquared =
- VecMagSq(this->fPosition.h - this->fNaybs.fAvgPos.h,
- this->fPosition.v - this->fNaybs.fAvgPos.v );
-
- // if we're near home, stop
- if(this->fNaybs.fAvgDistSquared <= 25)
- this->fHome = true;
-
- return this->fHome;
- }